﻿using System;
using System.Linq.Expressions;
using System.Reflection;

namespace NanoMvvm
{
    internal class PropertyHelper
    {
        internal static Func<object, object> CreateOpenGetter(PropertyInfo propInfo)
        {
            if (propInfo == null)
            {
                throw new ArgumentNullException("propInfo");
            }
            if (propInfo.GetGetMethod(true) == null || propInfo.GetGetMethod(true).IsStatic)
            {
                throw new InvalidOperationException("Invalid PropertyInfo.");
            }

            var instanceParameter = Expression.Parameter(typeof(object), "i");
            var convertedInstanceParameter = Expression.ConvertChecked(instanceParameter, propInfo.DeclaringType);

            var propertyExpression1 = Expression.Property(convertedInstanceParameter, propInfo);
            var convertedProperty = Expression.ConvertChecked(propertyExpression1, typeof(object));

            var get = Expression.Lambda<Func<object, object>>(
                convertedProperty,
                instanceParameter);

            return get.Compile();
        }

        internal static Action<object, object> CreateOpenSetter(PropertyInfo propInfo)
        {
            if (propInfo == null)
            {
                throw new ArgumentNullException("propInfo");
            }

            if (propInfo.GetSetMethod(true) == null || propInfo.GetSetMethod(true).IsStatic)
            {
                throw new InvalidOperationException("Invalid PropertyInfo.");
            }

            var instanceParameter = Expression.Parameter(typeof(object), "i");
            var convertedInstanceParameter = Expression.ConvertChecked(instanceParameter, propInfo.DeclaringType);

            var valueParameter = Expression.Parameter(typeof(object), "v");
            var convertedValueParameter = Expression.ConvertChecked(valueParameter, propInfo.PropertyType);

            var propertyExpression = Expression.Property(convertedInstanceParameter, propInfo);

            var set =
                Expression.Lambda<Action<object, object>>(
                    Expression.Assign(propertyExpression, convertedValueParameter), instanceParameter, valueParameter);
            return set.Compile();
        }

        internal static Func<object> CreateStaticGetter(PropertyInfo propInfo)
        {
            if (propInfo == null)
            {
                throw new ArgumentNullException("propInfo");
            }

            if (!propInfo.GetGetMethod(true).IsStatic)
            {
                throw new InvalidOperationException("Invalid PropertyInfo.");
            }

            if (propInfo.GetGetMethod(true) == null)
            {
                return null;
            }

            var theProperty = Expression.Property(null, propInfo);

            var convertedProperty = Expression.ConvertChecked(theProperty, typeof(object));

            var get = Expression.Lambda<Func<object>>(
                convertedProperty);

            return get.Compile();
        }

        internal static Action<object> CreateStaticSetter(PropertyInfo propInfo)
        {
            if (propInfo == null)
            {
                throw new ArgumentNullException("propInfo");
            }

            if (!propInfo.GetGetMethod(true).IsStatic)
            {
                throw new InvalidOperationException("Invalid PropertyInfo.");
            }

            if (propInfo.GetSetMethod(true) == null)
            {
                return null;
            }

            var theProperty = Expression.Property(null, propInfo);

            var valueParameter = Expression.Parameter(typeof(object), "v");
            var convertedValueParameter = Expression.ConvertChecked(valueParameter, propInfo.PropertyType);

            var set = Expression.Lambda<Action<object>>(
                Expression.Assign(theProperty, convertedValueParameter), valueParameter);
            return set.Compile();
        }
    }
}